﻿using Furion.DatabaseAccessor;
using Furion.DatabaseAccessor.Extensions;
using Furion.DependencyInjection;
using Furion.DynamicApiController;
using Furion.FriendlyException;
using LimaLims.Application.Service.Basic.Dto;
using LimaLims.Application.Service.Sys;
using LimaLims.Application.Service.TestCenter.Dto;
using LimaLims.Core;
using LimaLims.Core.Entity.Basic;
using LimaLims.Core.Entity.Equipment;
using LimaLims.Core.Entity.TestCenter;
using LimaLims.Core.Enum;
using LimaLims.Core.Util;
using Mapster;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Yitter.IdGenerator;

namespace LimaLims.Application.Service.TestCenter
{
    /// <summary>
    ///试验计划服务API
    /// </summary>
    [ApiDescriptionSettings(Name = "ExperimentPlan", Order = 157)]
    [Route("api")]
    public class ExperimentPlanService : IExperimentPlanService, IDynamicApiController, ITransient
    {
        private readonly IRepository<ExperimentPlan> _experimentPlanRep;
        private readonly IRepository<ExperimentTask> _experimentTaskRep;
        private readonly IRepository<ExperimentPlanMaterial> _experimentPlanMaterialRep;
        private readonly IRepository<ExperimentPlanProduct> _experimentPlanProductRep;
        private readonly IGenerateCodeUtilService _generateCodeUtilService;//编码规则
        private readonly IRepository<InspectionData> _inspectionDataRep;
        private readonly IRepository<Product> _productRep;
        private readonly IRepository<Material> _materialRep;

        /// <summary>
        /// 构造函数
        /// </summary>
        public ExperimentPlanService(IRepository<ExperimentPlan> experimentPlanRep, IRepository<ExperimentTask> experimentTaskRep, IRepository<ExperimentPlanMaterial> experimentPlanMaterialRep, IRepository<ExperimentPlanProduct> experimentPlanProductRep, IGenerateCodeUtilService generateCode, IRepository<InspectionData> inspectionDataRep, IRepository<Product> productRep, IRepository<Material> materialRep)
        {
            _experimentPlanRep = experimentPlanRep;//试验计划
            _experimentTaskRep = experimentTaskRep;//试验任务
            _experimentPlanMaterialRep = experimentPlanMaterialRep;
            _experimentPlanProductRep = experimentPlanProductRep;
            _generateCodeUtilService = generateCode;
            _inspectionDataRep = inspectionDataRep;
            _productRep = productRep;
            _materialRep = materialRep;
        }

        /// <summary>
        /// 更新计划详细
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        private async Task UpdateExperimentPlanData(AddExperimentPlanInput input)
        {
            var oldMaterialItems = await _experimentPlanMaterialRep.DetachedEntities.Where(u => u.ExperimentPlanId == input.Id).ToListAsync();

            await _experimentPlanMaterialRep.DeleteAsync(oldMaterialItems);

            var oldProductItems = await _experimentPlanProductRep.DetachedEntities.Where(u => u.ExperimentPlanId == input.Id).ToListAsync();

            await _experimentPlanProductRep.DeleteAsync(oldProductItems);

            if (input.Type == ExperimentType.Product && input.ProductIds.Any())
            {
                var productItems = input.ProductIds.Select(u => new ExperimentPlanProduct
                {
                    Id = YitIdHelper.NextId(),
                    ExperimentPlanId = input.Id,
                    ProductId = u
                }).ToList();

                await _experimentPlanProductRep.InsertAsync(productItems);
            }

            if (input.Type == ExperimentType.Material && input.MaterialIds.Any())
            {
                var productItems = input.MaterialIds.Select(u => new ExperimentPlanMaterial
                {
                    Id = YitIdHelper.NextId(),
                    ExperimentPlanId = input.Id,
                    MaterialId = u
                }).ToList();

                await _experimentPlanMaterialRep.InsertAsync(productItems);
            }
        }

        /// <summary>
        /// 新增
        /// </summary>
        [HttpPost("experimentPlan/add")]
        [UnitOfWork]
        public async Task Add(AddExperimentPlanInput input)
        {
            var isExist = await _experimentPlanRep.DetachedEntities.AnyAsync(u => u.Code == input.Code);
            if (isExist)
                throw Oops.Oh("已存在相同编号的计划！");

            if (string.IsNullOrWhiteSpace(input.Code))
            {
                input.Code = await _generateCodeUtilService.GenerateCode(BusinessType.ExperimentPlan);
            }

            input.Id = YitIdHelper.NextId();

            var experimentPlan = input.Adapt<ExperimentPlan>();

            experimentPlan.PlanStatus = ExperimentPlanStatus.NotStarted;

            await _experimentPlanRep.InsertNowAsync(experimentPlan);

            if (!input.ProductIds.Any() && !input.MaterialIds.Any())
            {
                throw Oops.Oh("请选择试验对应的产品或者物料！");
            }
            await UpdateExperimentPlanData(input);
        }

        /// <summary>
        /// 作废
        /// </summary>
        [HttpPost("experimentPlan/cancel")]
        public async Task Cancel(long id)
        {
            var experimentPlan = await _experimentPlanRep.DetachedEntities.FirstOrDefaultAsync(u => u.Id == id);
            if (experimentPlan == null)
                throw Oops.Oh("不存在此数据！");

            experimentPlan.PlanStatus = ExperimentPlanStatus.Invalid;

            await experimentPlan.UpdateAsync();
        }

        /// <summary>
        /// 删除
        /// </summary>
        [HttpPost("experimentPlan/delete")]
        [UnitOfWork]
        public async Task Delete(long id)
        {
            var experimentPlan = await _experimentPlanRep.DetachedEntities.FirstOrDefaultAsync(u => u.Id == id);
            if (experimentPlan == null)
                throw Oops.Oh("不存在此数据！");

            // 先删关联数据
            var materialItems = await _experimentPlanMaterialRep.DetachedEntities.Where(u => u.ExperimentPlanId == experimentPlan.Id).ToListAsync();

            await _experimentPlanMaterialRep.DeleteAsync(materialItems);

            var productItems = await _experimentPlanProductRep.DetachedEntities.Where(u => u.ExperimentPlanId == experimentPlan.Id).ToListAsync();

            await _experimentPlanProductRep.DeleteAsync(productItems);

            await _experimentPlanRep.DeleteAsync(experimentPlan);
        }

        /// <summary>
        /// 获取详细
        /// </summary>
        [HttpGet("experimentPlan/detail")]
        public async Task<ExperimentPlanDetail> Detail(long id)
        {
            var experimentPlan = await _experimentPlanRep.DetachedEntities.Include(s => s.Team).Include(s => s.InspectionScheme).FirstOrDefaultAsync(u => u.Id == id);
            if (experimentPlan == null)
                throw Oops.Oh("不存在此数据！");

            ExperimentPlanDetail detail = new ExperimentPlanDetail();

            detail.Scheme = experimentPlan.InspectionScheme;

            detail.PlanModel = experimentPlan.Adapt<ExperimentPlanModel>();

            detail.PlanModel.TeamName = experimentPlan.Team.Name;

            if (experimentPlan.Type == ExperimentType.Material)
            {
                detail.PlanModel.MaterialIds = await _experimentPlanMaterialRep.DetachedEntities.Where(u => u.ExperimentPlanId == experimentPlan.Id).Select(s => s.MaterialId).ToListAsync();
                detail.Materials = await _materialRep.DetachedEntities.Where(s => detail.PlanModel.MaterialIds.Contains(s.Id)).ToListAsync();
            }
            else
            {
                detail.PlanModel.ProductIds = await _experimentPlanProductRep.DetachedEntities.Where(u => u.ExperimentPlanId == experimentPlan.Id).Select(s => s.ProductId).ToListAsync();

                detail.Products = await _productRep.DetachedEntities.Include(s=>s.ProjectDrawing) .Where(s => detail.PlanModel.ProductIds.Contains(s.Id))
                    .Select(u => new ProductOutput
                    {
                        Id = u.Id,
                        Code = u.Code,
                        Name = u.Name,
                        ProjectName = u.ProjectDrawing.ProjectName,
                        FactoryNumbe = u.FactoryNumbe,
                        Specification = u.ProjectDrawing.Specification,
                        Unit = u.ProjectDrawing.Unit,
                        Remark = u.Remark,
                    })
                    .ToListAsync();
            }

            detail.InspectionTableData = await _inspectionDataRep.DetachedEntities.Where(s => s.InspectionSchemeId == experimentPlan.InspectionSchemeId).Select(u => new InspectionDataOutput
            {
                Id = u.Id,
                Code = u.Code,
                InspectionName = u.InspectionName,
                Remark = u.Remark,
                InspectionSchemeId = u.InspectionSchemeId,
                InspectionType = u.InspectionType,
                NsLowerLimit = u.NsLowerLimit,
                SjLowerLimit = u.SjLowerLimit,
                NsUpperLimit = u.NsUpperLimit,
                SjTargetValue = u.SjTargetValue,
                SjUpperLimit = u.SjUpperLimit,
                NsTargetValue = u.NsTargetValue,
            }).ToListAsync();

            return detail;
        }

        /// <summary>
        /// 编辑
        /// </summary>
        [HttpPost("experimentPlan/edit")]
        [UnitOfWork]
        public async Task Edit(EditExperimentPlanInput input)
        {
            var experimentPlan = await _experimentPlanRep.DetachedEntities.FirstOrDefaultAsync(u => u.Id == input.Id);
            if (experimentPlan == null)
                throw Oops.Oh("不存在此数据！");

            var isExist = await _experimentPlanRep.DetachedEntities.AnyAsync(u => u.Code == input.Code && u.Id != input.Id);
            if (isExist)
                throw Oops.Oh("已存在相同编号的计划！");

            var experimentPlanModel = input.Adapt<ExperimentPlan>();

            await experimentPlanModel.UpdateAsync();

            if (!input.ProductIds.Any() && !input.MaterialIds.Any())
            {
                throw Oops.Oh("请选择试验对应的产品或者物料！");
            }

            await UpdateExperimentPlanData(input);
        }

        /// <summary>
        /// 生成任务
        /// </summary>
        [HttpGet("experimentPlan/generate")]
        [UnitOfWork]
        public async Task Generate(long id)
        {
            var experimentPlan = await _experimentPlanRep.DetachedEntities.Include(s => s.Team).FirstOrDefaultAsync(u => u.Id == id);
            if (experimentPlan == null)
                throw Oops.Oh("不存在此数据！");

            if (experimentPlan.Type == ExperimentType.Material)
            {
                var materialItems = await _experimentPlanMaterialRep.DetachedEntities.Include(s => s.Material).Where(u => u.ExperimentPlanId == experimentPlan.Id).ToListAsync();
                if (materialItems.Any())
                {
                    List<ExperimentTask> tasks = new List<ExperimentTask>();
                    foreach (var item in materialItems)
                    {
                        ExperimentTask task = new ExperimentTask
                        {
                            Id = YitIdHelper.NextId(),
                            ExperimentPlanId = experimentPlan.Id,
                            TaskCode = await _generateCodeUtilService.GenerateCode(BusinessType.ExperimentTask),
                            Status = ExperimentStatus.NotStarted,
                            TaskType = ExperimentType.Material,
                            TargetId = item.MaterialId,
                            TargetName = item.Material.Name,
                            TargetCode = item.Material.Code,
                            PlanStartTime = experimentPlan.PlanStartTime,
                            PlanEndTime = experimentPlan.PlanEndTime,
                            TeamId = experimentPlan.TeamId,
                            TeamName = experimentPlan.Team.Name,
                        };
                        tasks.Add(task);
                    }
                    await _experimentTaskRep.InsertAsync(tasks);
                }
            }

            if (experimentPlan.Type == ExperimentType.Product)
            {
                var productItems = await _experimentPlanProductRep.DetachedEntities.Include(s => s.Product).Where(u => u.ExperimentPlanId == experimentPlan.Id).ToListAsync();

                if (productItems.Any())
                {
                    List<ExperimentTask> tasks = new List<ExperimentTask>();
                    foreach (var item in productItems)
                    {
                        ExperimentTask task = new ExperimentTask
                        {
                            Id = YitIdHelper.NextId(),
                            ExperimentPlanId = experimentPlan.Id,
                            FactoryNumbe= item.Product.FactoryNumbe,
                            TaskCode = await _generateCodeUtilService.GenerateCode(BusinessType.ExperimentTask),
                            Status = ExperimentStatus.NotStarted,
                            TaskType = ExperimentType.Product,
                            TargetId = item.ProductId,
                            TeamId = experimentPlan.TeamId,
                            TeamName = experimentPlan.Team.Name,
                            TargetName = item.Product.Name,
                            TargetCode = item.Product.Code,
                            PlanStartTime = experimentPlan.PlanStartTime,
                            PlanEndTime = experimentPlan.PlanEndTime,
                        };
                        tasks.Add(task);
                    }
                    await _experimentTaskRep.InsertAsync(tasks);
                }
            }

            experimentPlan.PlanStatus = ExperimentPlanStatus.ToBegin;
            //更新计划状态
            await _experimentPlanRep.UpdateIncludeAsync(entity: experimentPlan, new[] { nameof(experimentPlan.PlanStatus) });

        }

        /// <summary>
        /// 分页数据
        /// </summary>
        [HttpGet("experimentPlan/page")]
        public async Task<PageResult<ExperimentPlanOutput>> QueryPage([FromQuery] ExperimentPlanPageInput input)
        {
            var code = !string.IsNullOrEmpty(input.SearchValue?.Trim());

            var items = await _experimentPlanRep.DetachedEntities.Include(s => s.Team).Include(s => s.InspectionScheme)
                                         .Where((code, u => EF.Functions.Like(u.Code, $"%{input.SearchValue.Trim()}%")))
                                         .Where(input.Type.HasValue, u => u.Type == input.Type)
                                         .Where(input.InspectionSchemeId.HasValue, u => u.InspectionSchemeId == input.InspectionSchemeId)
                                         .Where(input.PlanStatus.HasValue, u => u.PlanStatus == input.PlanStatus)
                                         .OrderByDescending(u => u.CreatedTime)
                                         .Select(u => new ExperimentPlanOutput
                                         {
                                             Id = u.Id,
                                             Code = u.Code,
                                             PlanStartTime = u.PlanStartTime,
                                             PlanEndTime = u.PlanEndTime,
                                             PlanStatus = u.PlanStatus,
                                             ActualEndTime = u.ActualEndTime,
                                             ActualStartTime = u.ActualStartTime,
                                             InspectionSchemeId = u.InspectionSchemeId,
                                             InspectionSchemeCode = u.InspectionScheme.Code,
                                             Type = u.Type,
                                             TeamId = u.TeamId,
                                             TeamName = u.Team.Name,
                                             ValidStatus = u.ValidStatus,
                                             Count = u.Type == ExperimentType.Product ? u.ExperimentPlanProducts.Count : u.ExperimentPlanMaterials.Count,
                                             Sort = u.Sort,
                                             CreatedTime = u.CreatedTime,
                                             CreatedUserName = u.CreatedUserName,
                                             UpdatedTime = u.UpdatedTime,
                                             UpdatedUserName = u.UpdatedUserName,
                                         }).ToADPagedListAsync(input.PageNo, input.PageSize);

            return items;
        }
    }
}
